export function patch(oldVnode, vnode) {
  if (!oldVnode) {
    return createElem(vnode); // 如果没有el元素， 那就直接根据虚拟节点返回真实节点
  }
  if (oldVnode.nodeType == 1) {
    // console.log("真实元素");
    // 用vnode 来生成真实dom， 替换原本的dom元素

    const parentElem = oldVnode.parentNode;

    let elem = createElem(vnode); // 根据虚拟节点 创建元素

    // 在第一次渲染后， 是删除掉节点， 下次再使用时无法获取了
    parentElem.insertBefore(elem, oldVnode.nextSibling);

    parentElem.removeChild(oldVnode);
    return elem;
  } else {
    // 如果标签名称不一样， 直接删除老的换成新的即可
    console.log("diff", oldVnode);
    console.log("diff", vnode);
    if (oldVnode.tag !== vnode.tag) {
      // 可以通过vnode.el属性， 获取现在真实的dom元素
      // oldVnode.el.parentNode.removeChild(vnode.el, oldVnode.el); // vnode不是一个真实节点，需要转为真实节点后再处理
      oldVnode.el.parentNode.replaceChild(createElem(vnode), oldVnode.el);
    }
    let el = (vnode.el = oldVnode.el); // 表示当前新节点， 复用老节点
    // 如果两个虚拟节点是文本节点， 比较文本内容........
    if (vnode.tag == undefined) {
      // 新老都是文本
      if (oldVnode.text !== vnode.text) {
        el.textContent = vnode.text;
      }
      return;
    }
    // 如果标签一样， 比较属性, 传入新的虚拟节点， 和老的属性， 用新的属性更新老的属性
    patchProps(vnode, oldVnode.data); // 属性可能有删除的情况

    // 针对儿子进行处理
    // 1. 一方有儿子， 一方没有儿子
    // 2. 双方都有儿子
    // debugger;
    let oldChildren = oldVnode.children || [];
    let newChildren = vnode.children || [];
    if (oldChildren.length > 0 && newChildren.length > 0) {
      // 双方都有儿子
      // vue使用了双指针的方式来进行比对
      patchChildren(el, oldChildren, newChildren);
    } else if (newChildren.length > 0) {
      // 老的没儿子， 但是新的有儿子
      for (let i = 0; i < newChildren.length; i++) {
        let child = createElem(newChildren[i]); // 循环创建新节点
        el.appendChild(child);
      }
    } else if (oldChildren.length > 0) {
      // 老的有儿子， 但是新的没有
      el.innerHTML = ""; // 直接删除老节点
    } else {
    }
    return el;
  }
}

// vue的特点就是每个组件都有一个watcher， 当前组件中数据变化， 只需要更新当前组件的

function isSameVnode(oldVnode, newVnode) {
  return oldVnode.tag == newVnode.tag && oldVnode.key == newVnode.key;
}
// dom的生成： ast ==> render方法 ==> 虚拟节点==> 真实dom
// 更新时需要重新创建ast语法树吗？
// 如果动态的添加了节点（绕过了vue添加， vue监控不到的） 难道不需要重新ast吗？
// 后续数据变了， 只会操作自己管理的dom元素

// 如果直接操作dom 和 vue无关， 不需要重新创建ast语法树
function patchChildren(el, oldChildren, newChildren) {
  let oldStartIndex = 0;
  let oldStartVnode = oldChildren[0];
  let oldEndIndex = oldChildren.length - 1;
  let oldEndVnode = oldChildren[oldEndIndex];

  let newStartIndex = 0;
  let newStartVnode = newChildren[0];
  let newEndIndex = newChildren.length - 1;
  let newEndVnode = newChildren[oldEndIndex];

  const makeIndexByKey = (children) => {
    children.reduce((memo, current, index) => {
      if (current.key) {
        memo[current.key] = index;
      }
      return memo;
    }, {});
  };

  const keysMap = makeIndexByKey(oldChildren);

  while (oldStartIndex <= oldEndIndex && newStartIndex <= newEndIndex) {
    if (!oldStartVnode) {
      // 元素已经被移走了
      oldStartVnode = oldChildren[++oldStartIndex];
    } else if (!oldEndVnode) {
      oldEndVnode = oldChildren[--oldEndIndex];
    }
    // 同时循环新的节点 和 老的节点， 有一方循环完毕就结束了
    if (isSameVnode(oldStartVnode, newStartVnode)) {
      // 头头比较， 发现标签一致
      patch(oldStartVnode, newStartVnode);

      oldStartVnode = oldChildren[++oldStartIndex];
      newStartVnode = newChildren[++newStartIndex];
    } else if (isSameVnode(oldEndVnode, newEndVnode)) {
      patch(oldEndVnode, newEndVnode);
      oldEndVnode = oldChildren[--oldEndIndex];
      newEndVnode = newChildren[--newEndIndex];
    } else if (isSameVnode(oldStartVnode, newEndVnode)) {
      patch(oldStartVnode, newEndVnode);
      el.insertBefore(oldStartVnode.el, oldEndVnode.el.nextSibling); // 移动老的元素， 老的元素就被移走了
      oldStartVnode = oldChildren[++oldStartIndex];
      newEndVnode = newChildren[--newEndIndex];
    } else if (isSameVnode(oldEndVnode, newStartVnode)) {
      patch(oldEndVnode, newStartVnode);
      el.insertBefore(oldEndVnode.el, oldStartVnode.el); // 移动老的元素， 老的元素就被移走了
      oldEndVnode = oldChildren[--oldEndIndex];
      newStartVnode = newChildren[++newStartIndex];
    } else {
      // 乱序比对， 核心diff
      // 1. 需要根据key 和对应的索引， 将老的内容生成 映射表
      let moveIndex = keysMap[newStartVnode.key]; // 用新的 去 老的中进行查找
      if (moveIndex == undefined) {
        // 如果不能复用直接创建新的插入到老的节点开头处
        el.insertBefore(createElem(newStartVnode), oldStartVnode.el);
      } else {
        let moveNode = oldChildren[moveIndex];
        oldChildren[moveIndex] = null; // 子节点已经被移除了
        el.insertBefore(moveNode.el, oldStartVnode.el);
        patch(moveNode, newStartVnode);
      }
      newStartVnode = newChildren[++newStartIndex];
    }

    // 头尾比较 reverse
  }

  // 如果用户追加 ， 怎么办呢, 直接新增
  if (newStartIndex <= newEndIndex) {
    for (let i = newStartIndex; i < newEndIndex; i++) {
      // el.appendChild(createElem(newChildren[i]));
      // insertBefore 方法可以实现appendChild功能， insertBefore(node，null)
      // 看一下尾指针的下一个元素是否存在
      let anchor =
        newChildren[newEndIndex + 1] == null
          ? null
          : newChildren[newEndIndex + 1].el;
      el.insertBefore(createElem(newChildren[i]), anchor);
    }
  }

  if (oldStartIndex <= oldEndIndex) {
    for (let i = newStartIndex; i < newEndIndex; i++) {
      // 如果老的多， 将老的节点删除， 但是里面可能有null的情况
      if (oldChildren[i] !== null) {
        el.removeChild(oldChildren[i]);
      }
    }
  }
}

function patchProps(vnode, oldProps) {
  // 初始化
  let newProps = vnode.data || {};
  let el = vnode.el;
  // 如果老的属性有， 而新的没有直接删除
  let newStyle = newProps.style || {};
  let oldStyle = (oldProps && oldProps.style) || {};
  for (let key in oldStyle) {
    if (!newStyle[key]) {
      el.style[key] = "";
    }
  }
  for (let key in oldProps) {
    if (!newProps[key]) {
      el.removeAttribute(key);
    }
  }
  for (let key in newProps) {
    if (key === "style") {
      for (let styleName in newProps.style) {
        el.style[styleName] = newProps.style[styleName];
      }
    } else {
      el.setAttribute(key, newProps[key]);
    }
  }
  console.log(newProps);
}

function createComponent(vnode) {
  let i = vnode.data;
  if ((i = i.hook) && (i = i.init)) {
    i(vnode); // 调用init方法
  }
  if (vnode.componentInstance) {
    return true; // 有属性说明子组件new完毕了， 并且对应的真实DOM挂载到了componentInstance.$el
  }
}

// 创建真实节点
export function createElem(vnode) {
  let { tag, data, children, text, vm } = vnode;
  if (typeof tag === "string") {
    if (createComponent(vnode)) {
      return vnode.componentInstance.$el;
    } else {
      // 元素

      vnode.el = document.createElement(tag); // 虚拟节点会有一个el属性 对应真实节点
      patchProps(vnode);
      children &&
        children.forEach((item) => {
          vnode.el.appendChild(createElem(item));
        });
    }
  } else {
    vnode.el = document.createTextNode(text);
  }
  return vnode.el;
}
